package com.yzy.domain.strategy.service.armory;

import java.awt.Container;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.tomcat.util.bcel.Const;
import org.springframework.stereotype.Service;

import com.yzy.domain.strategy.model.entity.StrategyAwardEntity;
import com.yzy.domain.strategy.model.entity.StrategyEntity;
import com.yzy.domain.strategy.model.entity.StrategyRuleEntity;
import com.yzy.domain.strategy.repository.IStrategyRepository;
import com.yzy.types.common.Constants;
import com.yzy.types.enums.ResponseCode;
import com.yzy.types.exception.AppException;

import lombok.extern.slf4j.Slf4j;

/**
 * @author yangzhenyu05 <yangzhenyu05@kuaishou.com>
 * Created on 2023-12-24
 * @description 概率表生成，奖品ID抽奖获取
 */
@Service
@Slf4j
public class StrategyArmoryDispatch implements IStrategyArmory, IStrategyDispatch {
    @Resource
    private IStrategyRepository strategyRepository;

    @Override
    public boolean assembleLotteryStrategy(Long strategyId) {
        try {
            // 1. 查询策略配置[内部包含缓存获取过程]
            List<StrategyAwardEntity> strategyAwardEntities = strategyRepository.queryStrategyAwardList(strategyId);
            assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities);
            // 2. 权重策略配置 - 适用于 rule_weight 权重规则 [内部包含缓存获取过程]
            StrategyEntity strategyEntity = strategyRepository.queryStrategyEntityByStrategyId(strategyId);
            Optional<String> ruleWeight = Optional.ofNullable(strategyEntity.getRuleWeight());
            if (!ruleWeight.isPresent()) {
                return true;
            }
            StrategyRuleEntity strategyRuleEntity = strategyRepository.queryStrategyRule(strategyId, ruleWeight.get());
            if (ObjectUtils.isEmpty(strategyRuleEntity)) {
                throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(),
                        ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
            }
            Map<String, List<Integer>> ruleWeightValueMap = strategyRuleEntity.getRuleWeightValues();
            Set<String> keys = ruleWeightValueMap.keySet();
            for (String key : keys) {
                List<Integer> ruleWeightValues = ruleWeightValueMap.get(key);
                ArrayList<StrategyAwardEntity> strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
                strategyAwardEntitiesClone.removeIf(entity -> !ruleWeightValues.contains(entity.getAwardId()));
                assembleLotteryStrategy(String.valueOf(strategyId).concat(Constants.UNDERLINE).concat(key),
                        strategyAwardEntitiesClone);
            }
            return true;
        } catch (Exception e) {
            log.error("#StrategyArmoryDispatch#getRandomAwardId assembleLotteryStrategy error the strategyId is [{}]",
                    strategyId, e);
            return false;
        }
    }

    /**
     * 全装配策略
     *
     * @param key 策略ID
     * @param strategyAwardEntities 策略对应奖品信息
     */
    private void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities) {
        // 1. 获取最小概率值
        BigDecimal minAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);

        // 2.用 1 * 1000 获得概率范围，百分位、千分位、万分位
        BigDecimal rateRange = BigDecimal.valueOf(convert(minAwardRate.doubleValue()));

        // 3. 生成策略奖品概率查找表【这里指需要在List集合中，存放上对应的奖品占位即可，占位越多等于概率越高】
        List<Integer> strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());
        for (StrategyAwardEntity strategyAward : strategyAwardEntities) {
            Integer awardId = strategyAward.getAwardId();
            BigDecimal awardRate = strategyAward.getAwardRate();
            // 计算出每个概率值需要存放到查找表的数量，循环填充
            for (int i = 0; i < rateRange.multiply(awardRate).setScale(0, RoundingMode.CEILING).intValue(); i++) {
                strategyAwardSearchRateTables.add(awardId);
            }
        }
        // 4. 对存储的奖品进行乱序操作
        Collections.shuffle(strategyAwardSearchRateTables);
        // 5. 生成出Map集合，key值，对应的就是后续的概率值。通过概率来获得对应的奖品ID
        Map<Integer, Integer> shuffleStrategyAwardSearchRateTable = new LinkedHashMap<>();
        for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) {
            shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i));
        }
        // 6. 将结果存放到 Redis 中
        strategyRepository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(),
                shuffleStrategyAwardSearchRateTable);
    }

    private double convert(double min) {
        double current = min;
        double max = 1;
        while (current < 1) {
            current = current * 10;
            max = max * 10;
        }
        return max;
    }

    @Override
    public Integer getRandomAwardId(Long strategyId) {
        try {
            // 1. 获取概率范围
            int rateRange = strategyRepository.getRateRange(strategyId);
            // 2. 获取对应奖品ID
            return strategyRepository.getStrategyAwardAssemble(String.valueOf(strategyId),
                    new SecureRandom().nextInt(rateRange));
        } catch (Exception e) {
            log.error("#StrategyArmoryDispatch#getRandomAwardId getRandomAwardId error the strategyId is [{}]",
                    strategyId, e);
            return NumberUtils.INTEGER_ZERO;
        }
    }

    @Override
    public Integer getRandomAwardId(Long strategyId, String ruleWeightValue) {
        try {
            String key = String.valueOf(strategyId).concat("_").concat(ruleWeightValue);
            int rateRange = strategyRepository.getRateRange(key);
            return strategyRepository.getStrategyAwardAssemble(key, new SecureRandom().nextInt(rateRange));
        } catch (Exception e) {
            log.error("#StrategyArmoryDispatch#getRandomAwardId getRandomAwardId error the strategyId is [{}],the "
                    + "ruleWeightValue is [{}]", strategyId, ruleWeightValue, e);
            return NumberUtils.INTEGER_ZERO;
        }
    }

    @Override
    public Integer getRandomAwardId(String key) {
        // 分布式部署下，不一定为当前应用做的策略装配。也就是值不一定会保存到本应用，而是分布式应用，所以需要从 Redis 中获取。
        int rateRange = strategyRepository.getRateRange(key);
        // 通过生成的随机值，获取概率值奖品查找表的结果
        return strategyRepository.getStrategyAwardAssemble(key, new SecureRandom().nextInt(rateRange));
    }

    @Override
    public Boolean subtractionAwardStock(Long strategyId, Integer awardId) {
        // strategyId + awardId
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
        return strategyRepository.subtractionAwardStock(cacheKey);
    }

}
